package com.ibeetl.eval;

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.beetl.core.Configuration;
import org.beetl.core.GroupTemplate;
import org.beetl.core.exception.BeetlException;
import org.beetl.core.exception.ErrorInfo;
import org.beetl.core.resource.StringTemplateResourceLoader;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;

import com.ibeetl.eval.fn.LookupFunction;
import com.ibeetl.eval.worker.BlockNodeWorker;
import com.ibeetl.eval.worker.ExpressNodeWorker;
import com.ibeetl.eval.worker.ScriptNodeWorker;
import com.ibeetl.eval.worker.SimpleDecisionNodeWorker;
import com.ibeetl.eval.worker.SumNodeWorker;

/**
 * 
 * import com.ibeetl.eval.worker.ExpressNodeWorker; *
 * 目前不具备保留中间结算结果到TreeNode，导致不能互相引用
 * 
 * @author xiandafu
 *
 */
public class EvalEngine {
	public static GroupTemplate gt = null;
	public static Map<String, NodeWorker> nodeFactory = new HashMap<String, NodeWorker>();
	static {
		nodeFactory.put("sum", new SumNodeWorker());
		nodeFactory.put("express", new ExpressNodeWorker());
		nodeFactory.put("script", new ScriptNodeWorker());
		nodeFactory.put("block", new BlockNodeWorker());
		nodeFactory.put("treeDecision", new SimpleDecisionNodeWorker());
	}
	static {
		try {
			Configuration conf = Configuration.defaultConfiguration();
			conf.setEngine("com.ibeetl.eval.EvalTemplateEngine");
			gt = new GroupTemplate(new StringTemplateResourceLoader(), conf);
			gt.registerFunction("lookup", new LookupFunction());
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public TreeNode run(Reader reader, Map<String, Object> paras) throws Exception {
		Document doc = getXML(reader);
		Element workspaceEle = doc.getRootElement();
		Element projectEle = workspaceEle.getChild("project");
		Map defineVars = getProjectVarDefine(projectEle);
		Map ctxMap = this.merage(paras, defineVars);

		Element docEle = projectEle.getChild("doc");

		EvalContext ctx = new EvalContext(this, ctxMap);
		// TreeNode node = ctx.enter(docEle.getAttributeValue("name"), docEle);
		TreeNode node = this.processRule(docEle, ctx);
		// ctx.exist();
		return node;

	}

	public String validate(Reader reader) throws Exception {
		Document doc = getXML(reader);
		Element workspaceEle = doc.getRootElement();
		Element projectEle = workspaceEle.getChild("project");
		Element docEle = projectEle.getChild("doc");
		StringBuilder error = new StringBuilder();
		validateElement(docEle, error);
		return error.toString();

	}

	private void validateElement(Element ruleEle, StringBuilder error) {
		List<Element> list = ruleEle.getChildren();
		if (list.size() == 0) {
			String name = ruleEle.getAttributeValue("name");
			String f = ruleEle.getAttributeValue("f");
			if (f == null) {
				f = "express";
			}
			NodeWorker worker = nodeFactory.get(f);
			String script = worker.getScript(ruleEle);
			BeetlException exception = gt.validateScript(script);
			if (exception == null) {
				// 语法没问题
				return;
			}
			ErrorInfo errorInfo = new ErrorInfo(exception);
			error.append("[规则节点:" + name + "]");
			error.append("[错误符号:  ").append(errorInfo.getErrorTokenText()).append("  ]");
			error.append("[错误信息:" + errorInfo.getMsg() + "]");
			error.append("[错误行数:" + errorInfo.getErrorTokenLine() + "]");
			error.append("\n");
			return;
		}
		for (Element ele : list) {
			validateElement(ele, error);
		}

	}

	private Document getXML(Reader reader) throws Exception {
		SAXBuilder jdomBuilder = new SAXBuilder();

		Document jdomDocument = jdomBuilder.build(reader);
		return jdomDocument;
	}

	/**
	 * 得到每个评级预先定义的变量
	 * 
	 * @param projectEle
	 * @return
	 */
	private Map getProjectVarDefine(Element projectEle) {
		
		return Collections.emptyMap();
	}
	
	

	public NodeWorker getRuleNode(Element ruleEle) {
		String function = ruleEle.getAttributeValue("f");
		if (function == null) {
			function = "express";
		}
		NodeWorker node = nodeFactory.get(function);
		if (node == null) {
			throw new RuntimeException(function + " 不支持" + ruleEle.getAttributeValue("name"));
		}
		return node;
	}

	public List<TreeNode> processChildren(List<Element> list, EvalContext ctx) {
		List<TreeNode> rets = new ArrayList<TreeNode>();
		for (Element rule : list) {
			TreeNode object = processRule(rule, ctx);
			rets.add(object);
		}

		return rets;

	}

	public TreeNode processRule(Element ruleEle, EvalContext ctx) {
		// 变量名字
		String name = ruleEle.getAttributeValue("name");
		String f = ruleEle.getAttributeValue("f");
		if (f == null) {
			f = "express";
		}
		NodeWorker worker = nodeFactory.get(f);
		if (worker == null) {
			throw new XmlEvalException(ruleEle, "未能找到指定处理类");
		}
		TreeNode node = worker.execute(ruleEle, ctx);
		return node;

	}

	protected Map<String, Object> merage(Map<String, Object> para, String newName, Object newValue) {
		Map<String, Object> newMap = new HashMap<String, Object>();
		newMap.putAll(para);
		newMap.put(newName, newValue);
		return newMap;
	}

	protected Map<String, Object> merage(Map<String, Object> para, Map map) {
		Map<String, Object> newMap = new HashMap<String, Object>();
		// todo 变量已经定义
		newMap.putAll(para);
		newMap.putAll(map);
		return newMap;
	}

	protected Map<String, Object> newMap(Map<String, Object> para) {
		Map<String, Object> newMap = new HashMap<String, Object>();

		newMap.putAll(para);
		return newMap;
	}

}
